﻿using System;
using System.Reflection;
using System.Text;
using System.IO;
using System.Collections.Generic;
namespace FanaticGene.CommandLineSerializer
{
    /// <summary>
    /// 序列化。
    /// </summary>
    public static class CommandLine
    {
        /// <summary>
        /// 反序列化。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static T Deserialize<T>(Stream stream)
        {
            List<byte> bytes = new List<byte>();
            int tmpChar = 0;

            while (true)
            {
                tmpChar = stream.ReadByte();
                if (tmpChar == -1) break;
                bytes.Add((byte)tmpChar);
            }
            var str = Encoding.Default.GetString(bytes.ToArray());
            return Deserialize<T>(str);
        }

        /// <summary>
        /// 反序列化。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Deserialize<T>(string str)
        {
            if (str == null) throw new ArgumentNullException(nameof(str));

            var strList = DepartCommandLine(str);

            var argumentDic = new Dictionary<string, Tuple<ArgumentAttribute, PropertyInfo>>();
            PropertyInfo program = null;
            var type = typeof(T);
            var result = Activator.CreateInstance(type);

            foreach (var propertyInfo in type.GetProperties())
            {
                var hasProgramHeader = false;
                var hasArgument = false;

                foreach (Attribute att in propertyInfo.GetCustomAttributes(false))
                {
                    if (att is ArgumentAttribute)
                    {
                        if (hasArgument == true) throw new Exception($"{propertyInfo.Name}只能有一个 [Argument] 标记，请不要重复添加。");

                        var argAtt = att as ArgumentAttribute;
                        if (!argumentDic.ContainsKey(argAtt.Key))
                        {
                            argumentDic.Add(argAtt.Key, new Tuple<ArgumentAttribute, PropertyInfo>(argAtt, propertyInfo));
                            hasArgument = true;
                        }

                        else
                            throw new Exception($"{argAtt.Key}已存在，请删除重复的命令行键值。");

                    }
                    else if (att is ProgramAttribute)
                    {
                        if (hasProgramHeader == true) throw new Exception($"{propertyInfo.Name}只能有一个 [Program] 标记，请不要重复添加。");
                        hasProgramHeader = true;
                    }
                }

                if (hasProgramHeader == true && hasArgument == true) throw new Exception($"{propertyInfo.Name} [Program] 标记和 [Argument] 标记只能存在一个，请不要重复添加。");
                if (hasProgramHeader)
                {
                    if (program == null)
                        program = propertyInfo;
                    else
                        throw new Exception($"已存在一个主程序字段{program.Name}，类体中不能存在重复的 [Program] 标记");
                }

            }

            if (program == null) throw new Exception("无法找到主程序标记，请添加 [Program] 标记。");

            if (strList.Count == 0)
                throw new Exception("命令行不能为空。");
            program.SetValue(result, strList[0]);

            foreach (var tuple in argumentDic.Values)
            {
                var propertyInfo = tuple.Item2;
                var argument = tuple.Item1;
                var index = strList.IndexOf(argument.Key);
                var rstvalue = argument.DefaultValue;
                if (index == -1) // 无法找到。
                {
                    if (argument.HasValue)
                    {
                        rstvalue = argument.DefaultValue;
                    }
                    else
                    {
                        rstvalue = "false";
                    }
                }
                else
                {
                    // 有参数。
                    if (argument.HasValue)
                    {
                        if (index < strList.Count)
                        {
                            rstvalue = strList[index + 1];
                        }
                        else
                        {
                            throw new Exception($"没有在想要的位置找到{argument.Key}的值。");
                        }

                    }
                    else // 无参数。
                    {
                        rstvalue = "true";
                    }

                    if (argument.IsAbsolutePath)
                    {
                        if (!File.Exists(rstvalue))
                            throw new Exception($"无法找到{argument.Key}中的地址，请确保地址的合法性。");
                    }


                    propertyInfo.SetValue(result, rstvalue);
                }




            }

            return (T)result;
        }


        // 将命令行参数进行字段拆分。
        public static List<string> DepartCommandLine(string str)
        {
            var skipSpace = true;// 开启口
            int len = str.Length;
            var buffer = new StringBuilder();
            var strList = new List<string>();
            for (int i = 0; i < len; i++)
            {
                if (str[i] == '\"')
                {
                    buffer.Append(str[i]);
                    if (skipSpace == true)
                    {
                        skipSpace = false;
                    }
                    else // 在此找到发现此时跳过空格模式开启时，重新将功能关闭。
                    {
                        skipSpace = true;
                        strList.Add(buffer.ToString());
                        buffer.Clear();
                    }

                }
                else if (str[i] == ' ')
                {
                    if (skipSpace == false) // 不跳过空格。
                    {
                        buffer.Append(str[i]);
                    }
                    else
                    {
                        // 跳过空格情况下查看结束时的内容。
                        if (buffer.Length > 0)
                        {
                            strList.Add(buffer.ToString());
                            buffer.Clear();
                        }

                    }
                }
                else
                {
                    buffer.Append(str[i]);
                }

            }

            if (buffer.Length > 0)
            {
                strList.Add(buffer.ToString());
                buffer.Clear();
            }

            if (skipSpace == false)
                throw new Exception("引号无配对。");


            return strList;
        }


        public static void OutputStringList(List<string> strList)
        {
            int i = 0;
            foreach (var item in strList)
            {
                Console.BackgroundColor = i % 2 == 0 ? ConsoleColor.Green : ConsoleColor.Red;
                Console.Write($"{item}");
                Console.ResetColor();
                Console.Write(" ");
                i++;
            }
            Console.ResetColor();
            Console.WriteLine(); ;
        }

        public static string Serialize(object obj)
        {
            var sb = new StringBuilder();

            var type = obj.GetType();

            var argumentDic = new Dictionary<string, Tuple<ArgumentAttribute, PropertyInfo>>();
            PropertyInfo program = null;
            var result = Activator.CreateInstance(type);

            foreach (var propertyInfo in type.GetProperties())
            {
                var hasProgramHeader = false;
                var hasArgument = false;

                foreach (Attribute att in propertyInfo.GetCustomAttributes(false))
                {
                    if (att is ArgumentAttribute)
                    {
                        if (hasArgument == true) throw new Exception($"{propertyInfo.Name}只能有一个 [Argument] 标记，请不要重复添加。");

                        var argAtt = att as ArgumentAttribute;
                        if (!argumentDic.ContainsKey(argAtt.Key))
                        {
                            argumentDic.Add(argAtt.Key, new Tuple<ArgumentAttribute, PropertyInfo>(argAtt, propertyInfo));
                            hasArgument = true;
                        }

                        else
                            throw new Exception($"{argAtt.Key}已存在，请删除重复的命令行键值。");

                    }
                    else if (att is ProgramAttribute)
                    {
                        if (hasProgramHeader == true) throw new Exception($"{propertyInfo.Name}只能有一个 [Program] 标记，请不要重复添加。");
                        hasProgramHeader = true;
                    }
                }

                if (hasProgramHeader == true && hasArgument == true) throw new Exception($"{propertyInfo.Name} [Program] 标记和 [Argument] 标记只能存在一个，请不要重复添加。");
                if (hasProgramHeader)
                {
                    if (program == null)
                        program = propertyInfo;
                    else
                        throw new Exception($"已存在一个主程序字段{program.Name}，类体中不能存在重复的 [Program] 标记");
                }

            }

            if (program == null) throw new Exception("无法找到主程序标记，请添加 [Program] 标记。");


            sb.Append($"{program.GetValue(obj)} ");

            foreach (var tuple in argumentDic.Values)
            {
                var propertyInfo = tuple.Item2;
                var argument = tuple.Item1;
                var rstvalue = argument.DefaultValue;
                // 有参数。
                if (argument.HasValue)
                {
                    rstvalue = $"{argument.Key} {propertyInfo.GetValue(obj)}";
                }
                else // 无参数。
                {
                    rstvalue = $"{argument.Key}";
                }

                if (argument.IsAbsolutePath)
                {
                    if (!File.Exists(propertyInfo.GetValue(obj) as string))
                        throw new Exception($"无法找到{argument.Key}中的地址，请确保地址的合法性。");
                }

                sb.Append($"{rstvalue} ");

            }

            return sb.ToString();
        }

    }
}